home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The CICA Windows Explosion!
/
The CICA Windows Explosion! - Disc 2.iso
/
demo
/
igps_102.zip
/
PHONEVW.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1994-12-01
|
40KB
|
1,195 lines
///////////////////////////////////////////////////////////////////////////////////
// Internet Global Phone Project
// CPhoneView phonevw.cpp : implementation of the CPhoneView class
//
// The Phoneview class is responsible for user interface handling and low level
// audio handling (including compression/decompression via GSM).
//
////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 1993-1994 microWonders Inc. All rights reserved.
//
// AN OPEN INVITION TO BUILD UPON AND CONTRIBUTE TO THE PUBLIC TECHNOLOGY POOL:
// You are encouraged to redistribute, and build upon the technologies presented
// in this source module and the accompanying article in Dr. Dobb's Journal provided
// all the conditions listed in the MUSTREAD.TXT file, included with this
// distribution, are met.
////////////////////////////////////////////////////////////////////////////////////
// Full DUPLEX is not possible currently due to drivers restriction, either sound
// rec or play only
//
// June 11,1994 Eliminate lock-step transmit-receive requirement
// June 10,1994 Added fool-proof checks on button states to avoid
// crashes due to multiple button presses (SL)
// June 5, 1994 fixed occasional buffer drop out due to OnRecordDone resetting
// some variables incorrectly (SL)
// June 2, 1994 fixed playback always at length - sizeof(GSM_FRAME) problem (SL)
// May 20, 1994 separated OLE2 dependencies, no longer needs OLE DLLs (SL)
// May 10, 1994 added 16 bit sound support via #define HI_FIDELITY switch(SL)
//
//===================================================================================
//
// UMIST distribution Revisions
//
// Nov 30, 1994 changed sampling frequency to 11025hz (PA)
// fixed bug that crashed program if it finished compressing
// first block while second was still recording. (PA)
#include "stdafx.h"
#include "mmsystem.h"
#include "mphone.h"
#include "phonedoc.h"
#include "wsmin.h"
#include "socket.h"
#include "talksock.h"
#include "phonevw.h"
#include "gsm.h"
#include "dlg_host.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CPhoneView, CEditView)
BEGIN_MESSAGE_MAP(CPhoneView, CEditView)
//{{AFX_MSG_MAP(CPhoneView)
ON_MESSAGE(MM_WIM_CLOSE, OnWaveInClose)
ON_MESSAGE(MM_WIM_OPEN, OnWaveInOpen)
ON_MESSAGE(MM_WIM_DATA, OnWaveInData)
ON_MESSAGE(MM_WOM_DONE, OnWaveOutDone)
ON_MESSAGE(PHONEMSG_RECORD_DONE, OnRecordDone)
ON_MESSAGE(PHONEMSG_COMPRESS_DONE, OnCompressDone)
ON_MESSAGE(WM_WSANOTIFY, OnClientNotify)
ON_UPDATE_COMMAND_UI(ID_PHONE_RECORD, OnUpdatePhRecord)
ON_UPDATE_COMMAND_UI(ID_PHONE_SEND, OnUpdatePhSend)
ON_COMMAND(ID_PHONE_RECORD, OnPhRecord)
ON_COMMAND(ID_PHONE_SEND, OnPhSend)
ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CPhoneView construction/destruction
CPhoneView::CPhoneView()
{
// TODO: add construction code here
m_recording = FALSE;
m_transmitting = FALSE;
m_hWaveIn = 0;
m_hWaveOut = 0;
if (!(m_hGsm = gsm_create()))
{
AfxGetMainWnd()->MessageBox("Cannot initialize gsm?");
return;
}
// set up compression buffer
m_CompBufSize = 0L;
m_hCompress = GlobalAlloc(GHND , (DWORD) COMPRESSED_BUFSIZE);
if (!m_hCompress)
{
AfxGetMainWnd()->MessageBox("cannot allocate compress buffer!");
return;
}
m_CompressedBuf = (HPSTR) GlobalLock(m_hCompress);
if (!m_CompressedBuf)
{
GlobalUnlock(m_hCompress);
GlobalFree(m_hCompress);
AfxGetMainWnd()->MessageBox("cannot lock compress buffer!");
return;
}
((CPhoneApp *) AfxGetApp())->m_OnlyView = this; // hook idle thread
m_compressWork = FALSE; // no work for background threads
m_decompressWork = FALSE;
m_state.doneBuffer = FALSE; // nothing to do initially
m_state.maxIter = 0L;
m_state.linearCount = 0;
m_state.i = 0L;
m_state.lpWaveHdr = (LPWAVEHDR) 0;
m_state.lpData = (HPSTR) 0;
m_ListenerStarted = FALSE;
}
CPhoneView::~CPhoneView()
{
GlobalUnlock(m_hCompress);
GlobalFree(m_hCompress);
gsm_destroy(m_hGsm);
}
/////////////////////////////////////////////////////////////////////////////
// CPhoneView drawing
void CPhoneView::OnDraw(CDC* pDC)
{
CPhoneDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
}
/////////////////////////////////////////////////////////////////////////////
// CPhoneView diagnostics
#ifdef _DEBUG
void CPhoneView::AssertValid() const
{
CEditView::AssertValid();
}
void CPhoneView::Dump(CDumpContext& dc) const
{
CEditView::Dump(dc);
}
CPhoneDoc* CPhoneView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CPhoneDoc)));
return (CPhoneDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CPhoneView message handlers
void CPhoneView::OnUpdatePhRecord(CCmdUI* pCmdUI)
{
//
// Set appropriate states of the UI buttons
// after the recording button is pressed
if (!m_recording)
{
if(!m_transmitting)
{
pCmdUI->Enable(TRUE);
pCmdUI->SetCheck(FALSE);
}
else
pCmdUI->Enable(FALSE);
}
else // recording
{
pCmdUI->Enable(TRUE);
pCmdUI->SetCheck(TRUE);
}
}
void CPhoneView::OnUpdatePhSend(CCmdUI* pCmdUI)
{
// update user interface after the send button
// is pressed
if (m_transmitting)
{
pCmdUI->Enable(TRUE);
pCmdUI->SetCheck(TRUE);
}
else // not transmitting
{
if (m_recording)
{
pCmdUI->Enable(TRUE);
pCmdUI->SetCheck(FALSE);
}
else
{
pCmdUI->Enable(FALSE);
}
}
}
void CPhoneView::OnPhRecord()
{
UINT retval;
// Start the recording process by seizing the WaveIn Device
// and start sending two priming audio buffer to it
if ((!m_transmitting) && (!m_recording)) // prevent users from repeat presses!
{
m_recording = TRUE;
m_transmitting = FALSE;
PCMWAVEFORMAT format;
LPWAVEHDR lpWaveHdr;
#ifdef HIGH_FIDELITY
format.wf.wFormatTag = WAVE_FORMAT_PCM;
format.wf.nChannels = 1;
format.wf.nSamplesPerSec = 11025;
format.wf.nAvgBytesPerSec = 22050;
format.wf.nBlockAlign = 2;
format.wBitsPerSample = 16;
#else
format.wf.wFormatTag = WAVE_FORMAT_PCM;
format.wf.nChannels = 1;
format.wf.nSamplesPerSec = 11025;
format.wf.nAvgBytesPerSec = 11025;
format.wf.nBlockAlign = 1;
format.wBitsPerSample = 8;
#endif
// open the device, have message come back to frame window
if(::waveInOpen( (LPHWAVEIN) &m_hWaveIn, 0, (LPWAVEFORMAT) &format, (UINT) m_hWnd,(DWORD) 0,(DWORD) CALLBACK_WINDOW))
{
AfxGetMainWnd()->MessageBox("Cannot open audio input device!");
m_recording = FALSE;
return;
}
if(MakeWaveInBuffer(m_hWaveIn, lpWaveHdr, PHONE_WAVEIN_BUFSIZE)== FALSE)
{
// will announce message internally
m_recording = FALSE;
return;
}
if (retval = waveInAddBuffer(m_hWaveIn, lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Cannot add buffer to driver pool!");
DestroyWaveInBuffer(m_hWaveIn, lpWaveHdr);
m_recording = FALSE;
return;
}
// allocate 2nd buffer
if(MakeWaveInBuffer(m_hWaveIn, lpWaveHdr, PHONE_WAVEIN_BUFSIZE)== FALSE)
{
// will announce message internally
m_recording = FALSE;
return;
}
if (retval = waveInAddBuffer(m_hWaveIn, lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Cannot add buffer2 to driver pool!");
DestroyWaveInBuffer(m_hWaveIn, lpWaveHdr);
m_recording = FALSE;
return;
}
TRACE("Just sent the first 2 buffers to record!\n");
if (waveInStart(m_hWaveIn))
{
AfxGetMainWnd()->MessageBox("Cannot start recording!");
m_recording = FALSE;
return;
}
} // of not transmitting
}
void CPhoneView::OnPhSend()
{
// User pressed the Send button. It is time now to stop the recording
// thread and start the transmission thread; BUT remember that the compression
// thread maynot be finished yet! We synchronize without
// locking up by stopping the WaveIn (recording) device, and putting a
// 'completed' message into the message queue.
if (m_recording)
{
m_transmitting = TRUE;
m_recording = FALSE;
// stop recording
if( waveInStop(m_hWaveIn))
{
AfxGetMainWnd()->MessageBox("Something funny, cannot stop recording.");
m_transmitting = FALSE;
return;
}
if( waveInReset(m_hWaveIn))
{
AfxGetMainWnd()->MessageBox("Something funny, cannot reset.");
m_transmitting = FALSE;
return;
}
// can't process right the way, got to give the MM process
// some slack
PostMessage(PHONEMSG_RECORD_DONE);
}
}
LRESULT CPhoneView::OnCompressDone(WPARAM wParam, LPARAM lParam)
{
// Start the transmission process now that the background compression
// thread has completed.
m_TalkClient.Connect(TCPSOCK, m_ClientAddress, IGP_PORT,
30000, m_hWnd, NULL, 10, m_CompressedBuf,
m_CompBufSize ); // 30000 ms before timer ticks, timer currently not used
m_transmitting = TRUE;
m_CompBufSize = 0; // reset buffer size for next recording!
return 0L;
}
/////////////////////////////////////////////////
//
// Multimedia Message Handling
//
// Handle the low level audio
//
LRESULT CPhoneView::OnWaveInClose(WPARAM wParam, LPARAM lParam)
{
// Not used currently
//MessageBox("Hi, I'm Here!");
return 0L;
}
LRESULT CPhoneView::OnWaveInData(WPARAM wParam, LPARAM lParam)
{
UINT retval;
LPWAVEHDR lpWaveHdr;
// Each time this is called by the system, we have a newly digitized buffer in the
// WAVE_FORMAT_PCM format, ready for compression and transmission.
//
// We prep the buffer for deallocation, put it into an MFC object list for
// processing by the compression thread. If the recording process is still
// in progress, we supply the WaveIn device with more buffers to avoid choking
// it.
lpWaveHdr = (LPWAVEHDR) lParam;
// unprepare immediately
waveInUnprepareHeader(m_hWaveIn, lpWaveHdr, sizeof(WAVEHDR));
// Store Processed Buffers in our List Here
m_bufList.Add((DWORD) lParam);
TRACE("Got another buffer of %ld bytes, and %s.\n", lpWaveHdr->dwBytesRecorded,
(lpWaveHdr->dwBufferLength == lpWaveHdr->dwBytesRecorded) ? "sending another":
"stopping");
// Refill the Driver with at least another buffer!
// Only if it is not the end of recording. DO NOT send anymore buffer!
// NOTE: a very weak check for recording-in-progress, should be made stronger
if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength) // full buffer
{
// note the value of lpWaveHdr will be changed by the next call
if(MakeWaveInBuffer(m_hWaveIn, lpWaveHdr, PHONE_WAVEIN_BUFSIZE)== FALSE)
{
return 0L;
}
if (retval = waveInAddBuffer(m_hWaveIn, lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Recycling: Cannot add buffer to driver pool!");
DestroyWaveInBuffer(m_hWaveIn, lpWaveHdr);
m_recording = FALSE;
return 0L;
}
}
// tell background thread to start compressing if not already set
if (m_compressWork == FALSE)
{
m_compressWork = TRUE;
m_state.doneBuffer = FALSE;
}
return 0L;
}
LRESULT CPhoneView::OnWaveInOpen(WPARAM wParam, LPARAM lParam)
{
// Not used currently
//MessageBox("Hi, I'm Here!");
return 0L;
}
LRESULT CPhoneView::OnWaveOutDone(WPARAM wParam, LPARAM lParam)
{
LPWAVEHDR lpWaveHdr;
LPWAVEINST lpWaveInst;
BOOL LastBlock;
// Each time this is called by the system, we have another audio buffer
// finished playing. It is time to de-allocate the resources used by
// the audio buffer at this point.
//
// For the last audio block, we shut off the WaveOut device and reset
// playback statistics.
// clean up the returned block
lpWaveHdr = (LPWAVEHDR) lParam;
// close up everything if it is the last block
lpWaveInst = (LPWAVEINST) lpWaveHdr->dwUser;
LastBlock = lpWaveInst->LastBlock;
DestroyWaveOutBuffer(m_hWaveOut, lpWaveHdr); // clean the block
if ( LastBlock == TRUE)
{
waveOutClose(m_hWaveOut);
m_hWaveOut = 0;
// set up compression buffer
m_CompBufSize = 0L;
}
return 0L;
}
LRESULT CPhoneView::OnRecordDone(WPARAM wParam, LPARAM lParam)
{
// Message indicating that recording has completed. We simply
// turn off the WaveIn device at this point. Note that we cannot
// turn off the device any earlier because there may have been some
// more buffers to clear. Remember that we're simulating a multi-threaded
// behaviour with segmented sequentially executed code!
waveInClose(m_hWaveIn);
m_hWaveIn = 0; // free up the device for playing
TRACE("Closed input device.\n");
// We cannot do anymore here. If we open output device or start decompression,
// lockup WILL occur. Instead, we delegate the responsibility to the background
// threads processing in the idle loop.
return 0L;
}
////////////////////////////////////////////////////////////////////
// Audio Buffer Management Routines
//
// These are the 'standard' low level audio buffers allocation,
// priming, de-priming, and de-allocation routines. Extremely tedious
// and non-interesting, note also that error handling is very poor
// currently: we just pop up application model dialog boxes.
//
BOOL CPhoneView::MakeWaveInBuffer(HWAVEIN hWaveIn, LPWAVEHDR &lpWaveHdr, DWORD dwBufSize) // Create a New buffer
{
HGLOBAL hData, hWaveHdr, hWaveInst;
HPSTR lpData;
LPWAVEINST lpWaveInst;
// get the first buffer
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwBufSize);
if (!hData)
{
AfxGetMainWnd()->MessageBox("cannot allocate buffer!");
return FALSE;
}
if ((lpData = (HPSTR) GlobalLock(hData))== NULL)
{
AfxGetMainWnd()->MessageBox("cannot lock memory for buffer!");
GlobalFree(hData);
return FALSE;
}
/* Allocate a waveform data header. The WAVEHDR must be
* globally allocated and locked.
*/
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (!hWaveHdr)
{
GlobalUnlock( hData );
GlobalFree( hData );
MessageBox( "Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (!lpWaveHdr)
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalFree( hWaveHdr );
AfxGetMainWnd()->MessageBox( "Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
/* Allocate and set up instance data for waveform data block.
* This information is needed by the routine that frees the
* data block after it has been played.
*/
hWaveInst = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEINST));
if (!hWaveInst)
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalUnlock( hWaveHdr );
GlobalFree( hWaveHdr );
AfxGetMainWnd()->MessageBox( "Not enough memory for instance data.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
lpWaveInst = (LPWAVEINST) GlobalLock(hWaveInst);
if (!lpWaveInst)
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalUnlock( hWaveHdr );
GlobalFree( hWaveHdr );
GlobalFree( hWaveInst );
AfxGetMainWnd()->MessageBox("Failed to lock memory for instance data.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
lpWaveInst->hWaveInst = hWaveInst;
lpWaveInst->hWaveHdr = hWaveHdr;
lpWaveInst->hWaveData = hData;
/* Set up WAVEHDR structure and prepare it to be written to wave device.
*/
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwBufSize;
lpWaveHdr->dwBytesRecorded = 0L;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
lpWaveHdr->dwUser = (DWORD) lpWaveInst;
if(waveInPrepareHeader(hWaveIn, lpWaveHdr, sizeof(WAVEHDR)))
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalUnlock( hWaveHdr );
GlobalFree( hWaveHdr );
GlobalUnlock( hWaveInst );
GlobalFree( hWaveInst );
AfxGetMainWnd()->MessageBox( "Unable to prepare wave header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
BOOL CPhoneView::MakeWaveOutBuffer(HWAVEIN hWaveIn, HWAVEOUT hWaveOut, LPWAVEHDR &lpWaveHdr, DWORD dwBufSize)
{
/* Set up WAVEHDR structure and prepare it to be written to wave device.
lpWaveHdr->dwBufferLength = lpWaveHdr->dwBytesRecorded;
lpWaveHdr->dwBytesRecorded = 0L;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
// no need for hWaveIn since the header is unprepared before storage
if (waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Cannot transform inbuf to outbuf?");
return FALSE;
}
return TRUE;
*/
HGLOBAL hData, hWaveHdr, hWaveInst;
HPSTR lpData;
LPWAVEINST lpWaveInst;
// get the first buffer
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwBufSize);
if (!hData)
{
AfxGetMainWnd()->MessageBox("cannot allocate buffer!");
return FALSE;
}
if ((lpData = (HPSTR) GlobalLock(hData))== NULL)
{
AfxGetMainWnd()->MessageBox("cannot lock memory for buffer!");
GlobalFree(hData);
return FALSE;
}
/* Allocate a waveform data header. The WAVEHDR must be
* globally allocated and locked.
*/
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (!hWaveHdr)
{
GlobalUnlock( hData );
GlobalFree( hData );
MessageBox( "Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (!lpWaveHdr)
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalFree( hWaveHdr );
AfxGetMainWnd()->MessageBox( "Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
/* Allocate and set up instance data for waveform data block.
* This information is needed by the routine that frees the
* data block after it has been played.
*/
hWaveInst = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEINST));
if (!hWaveInst)
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalUnlock( hWaveHdr );
GlobalFree( hWaveHdr );
AfxGetMainWnd()->MessageBox( "Not enough memory for instance data.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
lpWaveInst = (LPWAVEINST) GlobalLock(hWaveInst);
if (!lpWaveInst)
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalUnlock( hWaveHdr );
GlobalFree( hWaveHdr );
GlobalFree( hWaveInst );
AfxGetMainWnd()->MessageBox("Failed to lock memory for instance data.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
lpWaveInst->hWaveInst = hWaveInst;
lpWaveInst->hWaveHdr = hWaveHdr;
lpWaveInst->hWaveData = hData;
lpWaveInst->LastBlock = FALSE; // most are not last block
/* Set up WAVEHDR structure and prepare it to be written to wave device.
*/
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwBufSize;
lpWaveHdr->dwBytesRecorded = 0L;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
lpWaveHdr->dwUser = (DWORD) lpWaveInst;
if(waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR)))
{
GlobalUnlock( hData );
GlobalFree( hData );
GlobalUnlock( hWaveHdr );
GlobalFree( hWaveHdr );
GlobalUnlock( hWaveInst );
GlobalFree( hWaveInst );
AfxGetMainWnd()->MessageBox( "Unable to prepare wave out header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
BOOL CPhoneView::DestroyWaveInBuffer(HWAVEIN hWaveIn, LPWAVEHDR lpWaveHdr)
{
LPWAVEINST lpWaveInst;
if (hWaveIn) // don't bother if not necessary
waveInUnprepareHeader(hWaveIn, lpWaveHdr, sizeof(WAVEHDR) );
lpWaveInst = (LPWAVEINST) lpWaveHdr->dwUser;
GlobalUnlock( lpWaveInst->hWaveData );
GlobalFree( lpWaveInst->hWaveData );
GlobalUnlock( lpWaveInst->hWaveHdr );
GlobalFree( lpWaveInst->hWaveHdr );
GlobalUnlock( lpWaveInst->hWaveInst );
GlobalFree( lpWaveInst->hWaveInst );
return (TRUE);
}
BOOL CPhoneView::DestroyWaveOutBuffer(HWAVEOUT hWaveOut, LPWAVEHDR lpWaveHdr)
{
LPWAVEINST lpWaveInst;
waveOutUnprepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR) );
lpWaveInst = (LPWAVEINST) lpWaveHdr->dwUser;
GlobalUnlock( lpWaveInst->hWaveData );
GlobalFree( lpWaveInst->hWaveData );
GlobalUnlock( lpWaveInst->hWaveHdr );
GlobalFree( lpWaveInst->hWaveHdr );
GlobalUnlock( lpWaveInst->hWaveInst );
GlobalFree( lpWaveInst->hWaveInst );
return (TRUE);
}
///////////////////////////////////////////////////////////////////////
// Compression and Expansion routines
//
// These are helper routines for the GSM compression and decompression.
//
LRESULT CPhoneView::CompressABuffer(HPSTR lpData, DWORD lsize)
{
ULONG maxIter;
//
// Compress the buffer pointed to by lpData of size lsize
// and store data into the internal m_CompressedBuf[]
//
maxIter = lsize / GSM_FRAME_SIZE; // do up to last GSM_FRAME_SIZE block
#ifdef HIGH_FIDELITY
maxIter /= 2; // 16 bit handling
#endif
ULONG linearCount = 0L;
for (ULONG i=0; i<maxIter; i++)
{
for (UINT j=0; j<GSM_FRAME_SIZE; j++)
{
short c;
#ifdef HIGH_FIDELITY
c = *((short far *)(lpData +linearCount));
// 16 bit samples are SIGNED!! so we don't need: c ^= 0x8000;
m_sample[j] = c;
linearCount += 2;
#else
c = ((short) (lpData[linearCount++] ^ 128));
m_sample[j] = (c << 8) + 8;
#endif
}
gsm_encode(m_hGsm, m_sample, m_GsmBuf);
AddOut((LPSTR) m_GsmBuf, sizeof(m_GsmBuf));
}
return 0L;
}
LRESULT CPhoneView::CompressPartialBuffer(LPTSTATE state)
{
// Yuk! This is part of a 'flattened' multi-loop used by the background thread
// (in the idle loop) to perform a 'partial buffer compression'
// each time an idle time slice is available. All done in the name
// of a 'better behaved' Windows application. It will perform one
// GSM frame worth of compression and then return control back to
// the message pump as long as work is still available.
//
if (state->maxIter == 0)
{
state->maxIter = state->lpWaveHdr->dwBytesRecorded / GSM_FRAME_SIZE;
#ifdef HIGH_FIDELITY
state->maxIter /= 2; // 16 bit handling
#endif
state->linearCount = 0L;
state->i = 0L;
state->doneBuffer = FALSE;
TRACE("Did One Buffer with comp buf size at %lu.\n", m_CompBufSize);
}
if (state->i < state->maxIter)
{
// do a GSM FRAME
HPSTR lpData;
lpData = (HPSTR) state->lpWaveHdr->lpData;
for (UINT j=0; j<GSM_FRAME_SIZE; j++)
{
short c;
#ifdef HIGH_FIDELITY
c = *((short far *)(lpData + state->linearCount));
// 16 bit samples are SIGNED!! so we don't need: c ^= 0x8000;
m_sample[j] = c;
state->linearCount += 2;
#else
c = ((short) (lpData[state->linearCount++] ^ 128));
// lpData[linearCount] ^= 128; // switch unsigned to signed
// m_sample[j] = U2S(lpData[linearCount]);
m_sample[j] = (c << 8) + 8;
#endif
} // of for
gsm_encode(m_hGsm, m_sample, m_GsmBuf);
AddOut((LPSTR) m_GsmBuf, sizeof(m_GsmBuf));
state->i += 1;
return 0L;
} // of if state->i < state->maxIter
else
{
state->maxIter = 0;
state->doneBuffer = TRUE;
DestroyWaveInBuffer(m_hWaveIn, state->lpWaveHdr);
return 1L; // completed
}
}
void CPhoneView::AddOut(LPSTR sbuf, UINT lsize)
{
// Data Mover Helper, make the main code somewhat less messy
//
// add an GSM frame or buffer to the CompressedBuf
for (UINT j=0; j<lsize; j++)
{
m_CompressedBuf[m_CompBufSize++] = sbuf[j];
}
}
LRESULT CPhoneView::ExpandWaveOut(HGLOBAL hMem, HPSTR CompressedBuf, ULONG CompBufSize)
{
UINT retval;
PCMWAVEFORMAT format;
// The Listener has spawned a server and the server has received all the compressed
// audiodata. We now prime and open the WaveOut device for playback, prep an audio
// buffer, and start the background de-compression thread on its merry way.
//
// Open Audio Output Device
#ifdef HIGH_FI_OUT
format.wf.wFormatTag = WAVE_FORMAT_PCM;
format.wf.nChannels = 1;
format.wf.nSamplesPerSec = 11025;
format.wf.nAvgBytesPerSec = 22050;
format.wf.nBlockAlign = 2;
format.wBitsPerSample = 16;
#else
format.wf.wFormatTag = WAVE_FORMAT_PCM;
format.wf.nChannels = 1;
format.wf.nSamplesPerSec = 11025;
format.wf.nAvgBytesPerSec = 11025;
format.wf.nBlockAlign = 1;
format.wBitsPerSample = 8;
#endif /* don't implement yet... */
// Try and open audio output device. If failure, should try again later
// just in case it is currently recording.
if(retval = waveOutOpen( (LPHWAVEOUT) &m_hWaveOut, 0, (LPWAVEFORMAT) &format,
(UINT) m_hWnd,(DWORD) 0,(DWORD) CALLBACK_WINDOW))
{
AfxGetMainWnd()->MessageBox("Cannot open audio output device!");
m_transmitting = FALSE;
return 0L;
}
// else
// {
// SHOULD HANDLE DELAYED-RETRY HERE ON A TIMER IN THE FUTURE
// }
TRACE("Begin expansion...\n");
if(MakeWaveOutBuffer(m_hWaveIn, m_hWaveOut, m_dstate.lpWaveHdr, (DWORD)PHONE_WAVEOUT_BUFSIZE)== FALSE)
{
AfxGetMainWnd()->MessageBox("Cannot allocate waveout buffers.");
return 0L;
}
// set up background thread states and let idle-processing handle it
m_dstate.lpData = m_dstate.lpWaveHdr->lpData;
m_dstate.linear = 0; // counter for Play Buffer fill
m_dstate.b = 0; // counter for GSM FRAME fill
m_dstate.CompressedBuf = CompressedBuf;
m_dstate.CompBufSize = CompBufSize;
m_dstate.i = 0;
m_decompressWork = TRUE;
m_hspeechBuffer = hMem; // used for memory deallocation later
return 0L;
}
LRESULT CPhoneView::PlayWaveInBuffer(LPWAVEHDR lpWaveHdr)
{
LPWAVEINST lpWaveInst;
lpWaveInst = (LPWAVEINST) lpWaveHdr->dwUser;
lpWaveInst->LastBlock = FALSE;
UINT retval;
//
// routine used in testing only
lpWaveHdr->dwBufferLength = lpWaveHdr->dwBytesRecorded;
lpWaveHdr->dwBytesRecorded = 0L;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
if(waveOutPrepareHeader(m_hWaveOut, lpWaveHdr, sizeof(WAVEHDR)))
{
DestroyWaveOutBuffer(m_hWaveOut, lpWaveHdr);
AfxGetMainWnd()->MessageBox( "Unable to prepare wave out header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return 1L;
}
if (retval = waveOutWrite(m_hWaveOut, lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Cannot write buffer to output device!");
DestroyWaveOutBuffer(m_hWaveOut, lpWaveHdr);
m_transmitting = FALSE;
return 1L;
}
return 0L;
}
BOOL CPhoneView::DoIdleProcessing(void)
{
UINT retval;
//
// This function is called everything the system is Idle. We use this
// chance to perform our background compression and de-compression.
//
// Both of these 'background threads' are multi-nested-loops which are
// 'flattened' in order to perform a very small unit of work on every
// idle opportunity. This keeps the application well behaving under
// Windows.
//
UINT numBuf = m_bufList.GetSize();
if (m_ListenerStarted == FALSE)
{
// start up listener if not started
m_TalkListener.StartListener(NULL, IGP_PORT, (LPWSADDRESS) &m_ListenerAddress, m_hWnd, NULL, this);
m_ListenerStarted = TRUE;
m_ClientAddress = m_TalkListener.MyAddress();
GetEditCtrl().SetSel(-1,-1);
GetEditCtrl().ReplaceSel((LPSTR) "IGP Listener started\r\n");
}
// THE COMPRESSION THREAD
// We have very little time, really
// do just 1 GSM frame
if (m_compressWork == TRUE)
{
if (m_state.doneBuffer == TRUE)
{
m_state.doneBuffer = FALSE;
// can't check for 1 left, since recording maybe in progress
if (( m_recording == FALSE) && (numBuf <=1))
{
LPWAVEHDR lpWaveHdr = (LPWAVEHDR) m_bufList[0];
DestroyWaveInBuffer(m_hWaveIn,lpWaveHdr);
m_bufList.RemoveAll();
TRACE("Pull last buffer.\n");
m_transmitting = TRUE;
m_compressWork = FALSE;
// reset here, otherwise face major re-entrancy problems
m_state.doneBuffer = FALSE; // nothing to do initially
m_state.maxIter = 0L;
m_state.linearCount = 0;
m_state.i = 0L;
m_state.lpWaveHdr = (LPWAVEHDR) 0;
m_state.lpData = (HPSTR) 0;
// signifies completion of background compress thread
PostMessage(PHONEMSG_COMPRESS_DONE);
return 0;
}
else
{
if (numBuf >= 1)
{
TRACE("Pull a buffer because DoneFlag!\n");
// load up new buffer
m_state.lpWaveHdr = (LPWAVEHDR) m_bufList[0];
m_state.lpData = (HPSTR) m_state.lpWaveHdr->lpData;
m_bufList.RemoveAt(0);
}
} // of else
} // of Done Buffer == TRUE
else // first time only
{
if ((numBuf >=1) && (m_state.lpWaveHdr == (LPWAVEHDR) 0))
{
TRACE("First time only pull buffer!\n");
// load up new buffer
m_state.lpWaveHdr = (LPWAVEHDR) m_bufList[0];
m_state.lpData = (HPSTR) m_state.lpWaveHdr->lpData;
m_bufList.RemoveAt(0);
}
}
if (((m_recording == TRUE) && (numBuf >= 1))
|| ((m_recording == FALSE)/* && (numBuf <=1)*/)) // during recording, numbuf may = 0
CompressPartialBuffer((LPTSTATE) &m_state);
return 1;
} // of m_compressWork
// THE DECOMPRESSION THREAD
// We have very little time, do just 1 GSM frame.
// Everytime we finish decompression a complete audio buffer,
// we'll send it to WaveOut for playback from here.
if (m_decompressWork == TRUE)
{
//for( UINT i=0; i<CompBufSize; i++)
//if (m_dstate.i < m_dstate.CompBufSize)
for (UINT i=0; (i<sizeof(m_GsmBuf)) && (m_dstate.i < m_dstate.CompBufSize);
i++)
{
m_GsmBuf[i] = m_dstate.CompressedBuf[m_dstate.i];
m_dstate.i++;
}
gsm_decode(m_hGsm, m_GsmBuf, m_sample); // no error check here for simplicity
for (UINT aa =0; aa < GSM_FRAME_SIZE; aa++) {
#ifdef HIGH_FI_OUT
// 16 bit samples are signed automagically
*((short far *) (m_dstate.lpData + m_dstate.linear)) = m_sample[aa];
m_dstate.linear += 2;
#else
// 8 bit samples need are unsigned and need changes
char c = (char) ((m_sample[aa]) >> 8) ;
// unsigned char c = S2U(m_sample[aa]);
c ^= 128; // put it back to unsigned
m_dstate.lpData[m_dstate.linear++] = c;
#endif
} // of for aa
if (m_dstate.i < m_dstate.CompBufSize)
{
if (m_dstate.linear == PHONE_WAVEOUT_BUFSIZE)
{
if (m_dstate.i == m_dstate.CompBufSize - 1) // special case of alignment
{
// mark last block
m_dstate.lpWaveInst = (LPWAVEINST) m_dstate.lpWaveHdr->dwUser;
m_dstate.lpWaveInst->LastBlock = TRUE;
}
m_dstate.linear = 0L;
TRACE("Printing 32 output buffer values:\n");
/* for (UINT j=0; j<32; j++)
{
TRACE ("C:%x ", (unsigned)lpData[j]); }
TRACE("\n");
*/
// send the data to play
if (retval = waveOutWrite(m_hWaveOut, m_dstate.lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Cannot write buffer to output device!");
DestroyWaveOutBuffer(m_hWaveOut, m_dstate.lpWaveHdr);
return 0L;
}
TRACE("Decompressed and sent a buffer of size %ld to output.\n",
m_dstate.lpWaveHdr->dwBufferLength);
// sent one, make another buffer ready
if(MakeWaveOutBuffer(m_hWaveIn, m_hWaveOut, m_dstate.lpWaveHdr,
(DWORD)PHONE_WAVEOUT_BUFSIZE)== FALSE)
{
AfxGetMainWnd()->MessageBox("Cannot allocate waveout buffers.");
return 0L;
}
m_dstate.lpData = m_dstate.lpWaveHdr->lpData; // vital -> forgot this in a bug?
} // of linear == PHONEWAVEOUT SIZE
return 1;
} // of (if i < compbufSize)
else
{ // i >= compbufSize
if (m_dstate.linear != 0L)
{
// handle final block
m_dstate.lpWaveInst = (LPWAVEINST) m_dstate.lpWaveHdr->dwUser;
m_dstate.lpWaveInst->LastBlock = TRUE;
m_dstate.lpWaveHdr->dwBufferLength = m_dstate.linear;
if (retval = waveOutWrite(m_hWaveOut, m_dstate.lpWaveHdr, sizeof(WAVEHDR)))
{
AfxGetMainWnd()->MessageBox("Cannot write buffer to output device!");
DestroyWaveOutBuffer(m_hWaveOut, m_dstate.lpWaveHdr);
m_decompressWork = FALSE;
m_transmitting = FALSE;
return 0L ;
}
m_decompressWork = FALSE;
// free the speech buffer now
if (m_hspeechBuffer)
{
GlobalUnlock(m_hspeechBuffer);
GlobalFree(m_hspeechBuffer);
}
TRACE("Printing 32 output buffer values:\n");
for (UINT j=0; j<32; j++)
{
TRACE ("C:%x ", (unsigned)m_dstate.lpData[j]);
}
TRACE("\n");
TRACE("Decompressed and sent LAST buffer of size %ld to output.\n",
m_dstate.lpWaveHdr->dwBufferLength);
} // of linear !=0
} // of else i>=bufsize
} // of decompressWork == TRUE
return 0;
}
////////////////////////////////////////////////////////////////////////
// Misc routines
//
void CPhoneView::OnFileOpen()
{
// Pop a dialog box and allow user to enter the IP address to talk to here
CDlgHostID abc;
abc.m_InetAddress = (ULONG) m_ClientAddress;
if (abc.DoModal() == IDOK)
m_ClientAddress = (WSADDRESS) WSNToHL(abc.m_InetAddress);
}
LRESULT CPhoneView::OnClientNotify(WPARAM wParam, LPARAM lParam)
{
// clearing house function for status, error messages and connection troubles
//
UINT event, errcode;
char tps[90]; // Pardon that magic number!:)
event = WSEVENT(lParam);
errcode = WSERROR(lParam);
switch(event)
{
case WSCONNECTED:
if (errcode == 0)
wsprintf(tps, "Client: Connected to Remote (%x).\r\n", errcode);
else
{
wsprintf(tps, "Client: Failed to connect to remote (%x).\r\n", errcode);
m_recording = FALSE;
m_transmitting = FALSE;
// m_CompBufSize = 0;
}
break;
case WSTALKBACK:
wsprintf(tps, "Client: Connect timer expired (%x).\r\n", errcode);
break;
case WSDISCONNECTED:
wsprintf(tps, "Client: Disconnected from remote (%x).\r\n", errcode);
m_hWaveIn = 0;
m_recording=FALSE;
m_transmitting = FALSE;
break;
case WSTALKLISCONNECT:
wsprintf(tps, "Listener: Detected remote client connection (%x).\r\n", errcode);
break;
case WSTALKLISFAILED:
wsprintf(tps, "Listener: Cannot start server, low memory (%x).\r\n", errcode);
break;
case WSCHILDCLOSED:
wsprintf(tps, "Server: Remote disconnected, server freed.(%x).\r\n", errcode);
break;
case WSTALKNEWCHILD:
wsprintf(tps, "Server: A new server is started (%x).\r\n", errcode);
break;
case WSTALKFAILED:
wsprintf(tps, "Client: Problem in protocol negotiation (%x).\r\n", errcode);
break;
default:
wsprintf(tps, "Client: Received event = %x, Error = %x\r\n", event, errcode);
break;
}
GetEditCtrl().SetSel(-1,-1);
GetEditCtrl().ReplaceSel((LPSTR) tps);
TRACE("WINDOW RECEIVED Code Event = %x, Error = %x\n", event, errcode);
return 0L;
}